#ifndef _PLC_PROGRAM_H_
#define _PLC_PROGRAM_H_

#include <windows.h>
#include <vector>

#include "types.h"
#include "opcodes.h"

class Program{
private:
	union instruction{
		struct binary{
			u8	b0 : 1;
			u8	b1 : 1;
			u8	b2 : 1;
			u8	b3 : 1;
			u8	b4 : 1;
			u8	b5 : 1;
			u8	b6 : 1;
			u8	b7 : 1;
		}bin[4];

		struct bytes{
			u8 b[4];
		}byt;

		u32 ins;
		
		instruction(){
			ins = 0;
	
			for(int i = 0; i < 4; i++){
				bin[i].b0 = 0;
				bin[i].b1 = 0;
				bin[i].b2 = 0;
				bin[i].b3 = 0;
				bin[i].b4 = 0;
				bin[i].b5 = 0;
				bin[i].b6 = 0;
				bin[i].b7 = 0;
			}

			for(int i = 0; i < 4; i++){
				byt.b[i] = 0;
			}
		}
	};

	std::vector<u32*> instruction_set;
	u32 *current_set;
	u32	count;

	instruction *cur_ins;
	u8 incremental;

public:
	Program(){
		this->current_set = (u32*)malloc(8192);
		memset(this->current_set, 0, 8192);
		this->instruction_set.push_back(current_set);

		this->count = 0;
		this->incremental = 1;
	};

	void setOpcode(u8 base_opcode){
		cur_ins = new instruction;

		cur_ins->byt.b[3] |= base_opcode;
	}

	void setImmediate(u8 type){
		cur_ins->bin[3].b0 = type;
	}

	void maskBytes(u64 byte, u8 num){
		if(num >= 1)
			cur_ins->byt.b[0] = byte & 0xFF;
		
		if(num >= 2)
			cur_ins->byt.b[1] = (byte & 0xFF00) >> 8;
		
		if(num >= 3)
			cur_ins->byt.b[2] = ((byte & 0xFF0000) >> 16);
	}

	void setRegister(u8 f, u8 r){
		cur_ins->byt.b[2] |= r << ((f==0)?4:0);
	}

	void endInstruction(){
		current_set[count] = cur_ins->ins;
		delete cur_ins;

		count += incremental;

		if(count >= 8192){
			current_set = (u32*)malloc(8192);
			memset(this->current_set, 0, 8192);
			instruction_set.push_back(current_set);

			count = 0;
		}
	}

	u32 getInstruction(){
		return cur_ins->ins;
	}

	std::vector<u32*> getInstructionSet(){
		return instruction_set;	
	};
	
	void setIncremental(u8 inc){
		incremental = inc;
	}

	u64 getCount(){
		return count;
	}
};

#endif